package com.stray.crawl.expr;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.stray.crawl.ResultContext;
import com.stray.crawl.TaskContext;
import com.stray.crawl.excp.ParseException;
import com.stray.crawl.utils.ReflectUtils;

public final class ExpressExecutor {

	private final Log log = LogFactory.getLog(this.getClass());
	private Map<String, Express> expresses = new HashMap<String, Express>();

	public ExpressExecutor() {
		try {
			Class<?> clazz = Express.class;
			String packageName = clazz.getPackage().getName();
			List<Class<?>> classes = ReflectUtils.getClasses(packageName);

			for (Class<?> subclass : classes) {
				if (subclass != clazz && clazz.isAssignableFrom(subclass)) {
					Express expr = (Express) subclass.newInstance();
					String name = expr.getName();

					expresses.put(name, expr);
				}
			}
		} catch (Exception ex) {
			log.warn("Initial class parser map failed. ", ex);
		}
	}

	public Object executeQuery(TaskContext taskContext, ResultContext resultContext, Object object, String query) {
		Object obj = object;

		if (query != null && !query.isEmpty()) {
			String[] phrases = query.split("/");

			EventData event = new EventData();

			event.setTaskContext(taskContext);
			event.setResultContext(resultContext);

			for (String phrase : phrases) {
				try {
					String name = extractName(phrase);
					String args = extractArguments(phrase);

					event.setObject(obj);
					event.setArguments(args);

					Express parser = expresses.get(name);
					if (parser != null) {
						obj = parser.doParse(event);
					}
				} catch (ParseException ex) {
					throw ex;
				} catch (Exception ex) {
					throw new ParseException("Error occur when parsing expression@query: " + phrase + "@" + query + " object: " + obj, ex);
				}
			}
		}

		return obj;
	}

	private String extractName(String phrase) {
		if (phrase == null || phrase.length() == 0) {
			return null;
		}

		int idx = phrase.indexOf('(');
		idx = idx > 0 ? idx : phrase.length();

		return phrase.substring(0, idx);
	}

	private String extractArguments(String phrase) {
		String args = null;

		if (phrase == null || phrase.length() == 0) {
			return null;
		}

		int fm = phrase.indexOf('(');
		int to = phrase.lastIndexOf(')');

		if (fm > 0 && to > 0) {
			args = phrase.substring(fm + 1, to);
		}

		return args;
	}

}